package com.autonavi.yunda.yunji.scheduled;

import com.autonavi.yunda.yunji.common.utils.JsonUtils;
import com.autonavi.yunda.yunji.core.engine.script.FunctionScriptClassManager;
import com.autonavi.yunda.yunji.core.enums.HealthCheckEnum;
import com.autonavi.yunda.yunji.core.model.dto.EngineHealthCheckDTO;
import com.autonavi.yunda.yunji.core.model.dto.HealthCheckLogDTO;
import com.autonavi.yunda.yunji.core.service.DebugService;
import com.autonavi.yunda.yunji.core.service.IIpService;
import com.autonavi.yunda.yunji.core.vo.node.BaseNode;
import com.autonavi.yunda.yunji.core.vo.node.HttpNode;
import com.autonavi.yunda.yunji.service.EngineHealthCheckService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.sql.Timestamp;
import java.util.List;
import java.util.UUID;

import static com.autonavi.yunda.yunji.common.constant.CommonConstant.MDC_TRACE_ID;

/**
 * @author sunzhenbang
 */
@Slf4j
@Component
@EnableScheduling
public class AdminScheduledService {
    @Resource
    private EngineHealthCheckService engineHealthCheckService;
    @Resource
    private IIpService skylineIpServiceImpl;
    @Resource
    private DebugService debugService;
    @Resource
    private AdminScheduledService adminScheduledService;
    @Resource
    private FunctionScriptClassManager functionScriptClassManager;

    @PostConstruct
    @Scheduled(cron = "0 0/5 * * * *")
    public void healthCheck() {
        List<Long> ids = engineHealthCheckService.selectAllId(HealthCheckEnum.OPEN);
        ids.forEach(id -> {
            try {
                adminScheduledService.check(engineHealthCheckService.findById(id));
            } catch (Exception e) {
                log.error("health_check", e);
            }

        });
    }

    @Scheduled(cron = "0 0/2 * * * *")
    public void checkAndRefreshFunctionScriptClassManager() {
        MDC.put(MDC_TRACE_ID, DigestUtils.md5DigestAsHex(UUID.randomUUID().toString().getBytes()));
        log.warn("checkAndRefreshFunctionScriptClassManager start");
        try {
            functionScriptClassManager.checkAndRefreshFunctionScriptManager();
        } catch (Exception e) {
            log.error("checkAndRefreshFunctionScriptClassManager error", e);
        } finally {
            log.warn("checkAndRefreshFunctionScriptClassManager end");
            MDC.remove(MDC_TRACE_ID);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void check(EngineHealthCheckDTO dto) {
        if (!isCheck(dto)) {
            return;
        }
        List<String> ipList = skylineIpServiceImpl.fetchEngineIpList();
        JsonNode request = JsonUtils.from(dto.getCheckData(), JsonNode.class);
        HttpNode baseNode = (HttpNode) BaseNode.parseNode(request);
        ipList.forEach(ip -> {
            baseNode.url = "http://" + ip + baseNode.url;
            engineHealthCheckService.update(EngineHealthCheckDTO.builder().id(dto.getId()).updateUser(dto.getUpdateUser()).checkStatus(HealthCheckEnum.CHECKING.name()).lastCheckTime(new Timestamp(System.currentTimeMillis())).build());
            try {
                debugService.debugNode(baseNode, null);
                engineHealthCheckService.update(EngineHealthCheckDTO.builder().id(dto.getId()).updateUser(dto.getUpdateUser()).checkStatus(HealthCheckEnum.HEALTH.name()).build());
            } catch (Exception e) {
                engineHealthCheckService.update(EngineHealthCheckDTO.builder().id(dto.getId()).updateUser(dto.getUpdateUser()).checkStatus(HealthCheckEnum.ERROR.name()).build());
                engineHealthCheckService.addErrorLog(HealthCheckLogDTO.builder().taskId(dto.getId()).interfaceId(dto.getInterfaceId()).ip(ip).response(e.getMessage()).createUser(dto.getUpdateUser()).updateUser(dto.getUpdateUser()).build());
            }
        });
    }

    private Boolean isCheck(EngineHealthCheckDTO dto) {
        return dto != null && HealthCheckEnum.OPEN.equals(dto.getOpen()) && (dto.getLastCheckTime() == null || dto.getLastCheckTime().getTime() + dto.getCheckTimeInterval() * 3600000 <= System.currentTimeMillis());
    }

}
